<?php
/**
 * Manipulate files in a chrooted environnement.
 */
class Files
{
	public $root = WWW_ROOT;
	
	function __construct($root=null)
	{
		if (!is_null($root)) {
			$this->root = $root;
		}
	}
	
	/**
	 * Checks wether a file or directory exists or not.
	 * 
	 * @return Boolean
	 * @param $path String
	 */
	function exists($path)
	{
		return file_exists($this->root.$path);
	}
	
	/**
	 * Creates a new directory.
	 * 
	 * @return Boolean
	 * @param $path String
	 */
	function mkdir($path)
	{
		return mkdir($this->root.$path);
	}
	
	/**
	 * Finds files and directories in a given path.
	 * 
	 * \code
	 * $Files = new Files(WWW_ROOT.'files'.DS);
	 * $data  = $Files->ls('path'.DS.'to'.DS.'list');
	 * $files = $data['files'];
	 * $dirs  = $data['dirs'];
	 * \endcode
	 * 
	 * @return Array
	 * @param $path String
	 * @param $recursive Inteher[optional]
	 */
	function & ls($path, $recursive=false)
	{
		$_path = $this->root.$path;
		
#		if (!isset($this->finfo)) {
#			$this->finfo = new finfo(FILEINFO_MIME, '/usr/share/file/magic');
#		}
		
		if (is_dir($_path) and ($dh = opendir($_path)))
		{
			$data = array(
				'dirs'  => array(),
				'files' => array()
			);
			
			while (false !== ($f = readdir($dh)))
			{
				if ($f == '.' or ($f == '..' and $path == '')) {
					continue;
				}
				if (is_dir($_path.DS.$f)) {
					$data['dirs'][] = $f;
				}
				else {
					$data['files'][] = $f;
				}
			}
			closedir($dh);
			
			array_multisort($data['dirs']);
			array_multisort($data['files']);
		}
		elseif (is_file($_path))
		{
			$data = stat($_path);
			$data['mimetype'] = $this->mimetype($_path);
#			$data['mimetype'] = mime_content_type($_path);
#			$data['mimetype'] = $this->finfo->file($_path);
		}
		return $data;
	}
	
	/**
	 * Returns mimetype of given file.
	 * 
	 * @return String
	 * @param $path String
	 */
	function mimetype($path)
	{
		if (preg_match('/\.([^.]+)$/', $path, $match))
		{
			switch(strtolower($match[1]))
			{
				# image
				case 'jpeg': case 'jpg': return 'image/jpeg';
				case 'png':     return 'image/gif';
				case 'gif':     return 'image/gif';
				case 'svg':     return 'image/svg+xml';
				
				# video
				case 'flv':     return 'video/x-flv';
				
				# audio
				case 'mp3':     return 'audio/mpeg';
				case 'ogg':     return 'application/ogg';
				
				# web
				case 'swf':     return 'application/x-shockwave-flash';
				case 'html':    return 'text/html';
				case 'css':     return 'text/css';
				case 'js':      return 'text/javascript';
				case 'vrml':    return 'model/vrml';
				
				# others
				case 'pdf':     return 'application/pdf';
				case 'txt':     return 'text/plain';
				case 'psd':     return 'application/x-photoshop';
				case 'torrent': return 'application/x-bittorrent';
			}
		}
	}
	
	/**
	 * Deletes either a file or a directory (recursively).
	 * 
	 * @return Boolean
	 * @param $path String
	 */
	function rm($path)
	{
		$_path = $this->root.$path;
		
		# a file?
		if (is_file($_path)) {
			return unlink($_path);
		}
		
		# a directory?
		elseif (is_dir($_path)) {
			return $this->rmdir($path);
		}
		
		# wtf?
		throw new Exception("$path: unknown file.", 0);
	}
	
	/**
	 * Recursively deletes a directory.
	 * 
	 * @return Boolean
	 * @param $path String
	 */
	function rmdir($path)
	{
		$_path = $this->root.$path;
		
		if (is_dir($_path) and ($dh = opendir($_path)))
		{
			while (false !== ($f = readdir($dh)))
			{
				if ($f == '.' or $f == '..') {
					continue;
				}
				elseif (is_dir($_path.$f))
				{
					if (!$this->rmdir($path.$f.DS)) {
						return false;
					}
				}
				else
				{
					if (!unlink($_path.$f)) {
						return false;
					}
				}
			}
			closedir($dh);
			return rmdir($_path);
		}
		elseif (file_exists($_path)) {
			return unlink($_path);
		}
		return true;
	}
	
	/**
	 * Recursively copies a directory.
	 * 
	 * @return Boolean
	 * @param $src String
	 * @param $dest String
	 */
	function copydir($src, $dest)
	{
		$_src  = $this->root.$src.DS;
		$_dest = $this->root.$dest.DS;
		
		if (!is_dir($_dest) and !mkdir($_dest)) {
			return false;
		}
		if ($dh = opendir($_src))
		{
			while(false !== ($f = readdir($dh)))
			{
				if ($f == '.' or $f == '..') {
					continue;
				}
				elseif (is_dir($_src.$f))
				{
					if (!$this->copydir($_src.$f, $_dest.$f)) {
						return false;
					}
				}
				elseif (is_file($_src.$f))
				{
					if (!copy($_src.$f, $_dest.$f)) {
						return false;
					}
					# sets modification date to reflect source's file date
					touch($_src.$f, filemtime($_dest.$f));
				}
			}
			closedir($dh);
		}
		return true;
	}
	
	/**
	 * Recursively finds files in a given path.
	 * 
	 * @return Array
	 * @param $path String Initial path to parse
	 * @param $extensions String[optional] A string of file extensions to search for (separated with pipes: '|').
	 * @param $listing Array[optional]
	 */
	function & find($path, $extensions=null, array $listing=array())
	{
		if (($dh = opendir($path)) !== false)
		{
			while(($fname = readdir($dh)) !== false)
			{
				if (substr($fname, 0, 1) == '.') {
					continue;
				}
				elseif (is_dir($path.$fname)) {
					self::find($path.$fname.DS, $extensions, &$listing);
				}
				elseif ($extensions !== null)
				{
					if (preg_match("/\.($extensions)$/", $fname)) {
						$listing[] = $path.$fname;
					}
				}
				else {
					$listing[] = $path.$fname;
				}
			}
			closedir($dh);
		}
		return $listing;
	}
	
	/**
	 * Returns given filename's extension (eg: jpg, pdf, odt).
	 * 
	 * @return String
	 * @param $filename String
	 */
	static function get_extension($filename)
	{
		return substr(strrchr($filename, '.'), 1);
	}
	
	/**
	 * Returns given filename without any extension.
	 * 
	 * @return String
	 * @param $filename String
	 */
	static function remove_extension($filename)
	{
		return substr($filename, 0, -strlen(strrchr($filename, '.')));
	}
	
	/**
	 * Returns human readable filesize.
	 * 
	 * @return String
	 * @param $size Integer
	 */
	static function format_size($size)
	{
		if ($size < 1024) {
			return sprintf(t('%d Bytes', 'global.file'), $size);
		}
		if ($size < 1024 * 1024) {
			return sprintf(t('%.2f KBytes', 'global.file'), $size / 1024);
		}
		if ($size < 1024 * 1024 * 1024) {
			return sprintf(t('%.2f MBytes', 'global.file'), $size / 1024 / 1024);
		}
		if ($size < 1024 * 1024 * 1024 * 1024) {
			return sprintf(t('%.2f GBytes', 'global.file'), $size / 1024 / 1024);
		}
	}
}
?>